package com.cogwheel.framework.graphics;

import java.awt.*;
import java.awt.image.*;
import javax.media.opengl.*;
import javax.media.opengl.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;

import com.cogwheel.framework.init.CPreferences;
import com.cogwheel.framework.util.*;
import com.cogwheel.framework.util.input.CKeyboard;
import com.cogwheel.framework.util.input.CMouse;
import com.jogamp.opengl.util.Animator;

/** Defines an OpenGL screen. Extend an empty class to use it. 
 *  @author Vandroiy
 *  @version 1.0
 */

public class CScreen extends JPanel implements GLEventListener {
	private static final long serialVersionUID = 1L;
	private static final String TAG = "COpenGLScreen";
	private CPreferences preferences = new CPreferences();
	private GLCanvas mCanvas;
	private long fpsLast = System.currentTimeMillis();
	private CMouse mouse = new CMouse();
	private CKeyboard keyboard = new CKeyboard();
	
	/** Main constructor
	 *  TODO: Window size, name, configuration file as parameter
	 *  DONE: Size of the window
	 */
	public CScreen(boolean iWindow, String config){
		//Creating the frame if necessary
		JFrame window = new JFrame();
		
		//Read the configuration file
		CConfiguration.CWGReadConfiguration(preferences.FOLDER_CONFIGURATION + config, preferences);
		
		// Setting up the panel
		this.setLayout(new BorderLayout());
		this.setSize(preferences.WINDOW_SIZE);
		
	
		if (iWindow){
			// Setting up the window
			window.setTitle(preferences.WINDOW_NAME);
			window.setSize(preferences.WINDOW_SIZE);
			window.setLayout(new BorderLayout());
			window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			window.setResizable(false);
		
		
			if (preferences.FULLSCREEN) window.setUndecorated(true);		
			CDebug.info(TAG, "Window created!");
		
			//Hide the cursor if the config says
			if (preferences.MOUSEDISABLED) CHideCursor();
			
			//Fullscreen
			if (preferences.FULLSCREEN) CFullscreen();
			
		}
		
		//OpenGL setup
		CWGSetupGL(iWindow, window);
	}
	
	/** CWGLSetupGL
	 *  Enables the OpenGL capabilities, and sets up a canvas.
	 *  CWGMouse and CWGKeyboard are registered here.
	 */
	private void CWGSetupGL(boolean iWindow, JFrame window){
		//Get graphics card capabilities, and sets the renderer to use db and hardware
		GLCapabilities mCaps = new GLCapabilities(null);
		mCaps.setHardwareAccelerated(true);
		mCaps.setDoubleBuffered(true);
		
		//Creating the canvas
		mCanvas = new GLCanvas(mCaps);
		mCanvas.addGLEventListener(this);
		
		//Adding the mouse to the canvas to be able to track motion and clicking
		mouse.CWGRegisterListener(mCanvas);
		keyboard.CRegisterListener(mCanvas);
		
		//Adding the canvas to the frame
		this.add(mCanvas, BorderLayout.CENTER);
		
		//Puts the rendering to a new thread, limiting it by 60 fps (default)
		Animator animator = new Animator(mCanvas);
		animator.start();
		
		//Showing the window and panel
		this.setVisible(true);
		if (iWindow){ window.add(this); window.setVisible(true); }
	}
	
	/** CWGHideCursor
	 *  It's important to hide the mouse cursor, because it's coordinates
	 *  are not presented right under Linux (maybe Windows too)
	 */
	private void CHideCursor(){
		// Transparent 16 x 16 pixel cursor image.
		BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);

		// Set the blank cursor to the JFrame.
		this.getRootPane().setCursor(Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(0, 0), "blank"));
	}
	
	/** CWGFullsceen
	 *  Set's the current window to exclusive fullscreen.
	 */
	private void CFullscreen(){
		System.setProperty("sun.java2d.noddraw", "true");
		GraphicsDevice gDevice = this.getGraphicsConfiguration().getDevice();
		Window window = Window.getWindows()[0];
		try{
			gDevice.setFullScreenWindow(window);
			CDebug.info(TAG, "Fullscreen mode on!");
		}finally{
			if (!preferences.FULLSCREEN)
				gDevice.setFullScreenWindow(null);
			
		 	CDebug.info(TAG, "Fullscreen mode off!");
		}
	}
	
	/** CWGUnregister
	 *  Removes the currently active listeners (keyboard, mouse)
	 */
	public void CUnregister(){
		mouse.CWGUnregisterListener(mCanvas);
		keyboard.CUnregisterListener(mCanvas);
	}
	
	/** CWGUpdate
	 *  Called by the main gamelogics loop to separate the calculations from
	 *  the OpenGL canvas's thread.
	 */
	public void CUpdate(CMouse mouse, CKeyboard keyboard){}
	
	/** CWGDrawScene 
	 *  @param gl GL2 to call OpenGL functions
	 */
	public void CDraw(GL2 gl){}
	
	/** CWGLoadScene
	 *  It's called when the window is created and UI/game elements can be added
	 *  @param gl GL2 to call OpenGL functions
	 */
	public void CInit(GL2 gl){}
	public void CLoad(GL2 gl){}
	public CPreferences CGetPreferences(){ return preferences; }
	/** CWGCalculateFPS
	 *  Called by the display method to measure FPS
	 *  @return String Measured FPS
	 */
	public int CWGCalculateFPS(){
		long fps = 1000/(System.currentTimeMillis() - fpsLast);
		fpsLast = System.currentTimeMillis();
		return (int)fps;
	}
	
	/** init
	 *  All OpenGL init comes here. Called when the program starts
	 */
	public void init(GLAutoDrawable drawable){
		CDebug.info(TAG, "I");
		
		//Get the actual OpenGL2.0
		GL2 gl = drawable.getGL().getGL2();

		//Some basic 2D setup
		gl.glClearColor(0, 0, 0, 0);
		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glShadeModel(GL2.GL_SMOOTH);
		gl.glEnable(GL2.GL_TEXTURE_2D);
		gl.glEnable(GL2.GL_BLEND);
		gl.glDisable(GL2.GL_DEPTH_TEST);
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
		
		gl.glLoadIdentity();
		gl.glOrtho(0, 1, 0, 1, -1, 1);
		
		//After-creation sprite loading for loading screen
		CAssets.LoadGraphics(gl);
		CLoad(gl);
		CInit(gl);
		
		//Game logics thread, limiting it's run to 60 exc/sec
		new Thread(new Runnable(){
			@Override
			public void run() {
				while(true){
					try {
						Thread.sleep(1000/60);
						CUpdate(mouse, keyboard);
					} catch (InterruptedException e) { e.printStackTrace(); }
				}
			}
		}).start();
	}
	
	/** reshape
	 *  Called when the windows size changes.
	 */
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height){}
	
	/** displayChanged
	 *  Called when the current screen changes
	 */
	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged){}
	
	/** display
	 *  Called when the renderer draws to the screen. Calls CWGDrawScene()
	 */
	public void display(GLAutoDrawable drawable){
		GL2 gl = drawable.getGL().getGL2();
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
		CDraw(gl);
	}
	
	/** dispose
	 *  Called when the screen disposes or the application exists.
	 */
	public void dispose(GLAutoDrawable drawable){
		this.setVisible(false);
	}
}
